package com.oceantrader.root.tools;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.oceantrader.root.tools.SqlLoader.SQL;

import io.vertx.core.Future;
import io.vertx.mysqlclient.MySQLClient;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.templates.SqlTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 数据库访问工具
 * 
 * @author xuesinuo
 */
@Slf4j
@RequiredArgsConstructor
public class Dao2Impl implements Dao2 {
    private final Pool pool;

    /** 开启一个事务 */
    public Future<Transaction> getTransaction() {
        return pool.getConnection().compose(vertxConnection -> vertxConnection.begin().map(vertxTransation -> new Transaction(vertxConnection, vertxTransation)));
    }

    /** 开启一个非事务链接 */
    public Future<Transaction> getConnection() {
        return pool.getConnection().map(vertxConnection -> new Transaction(vertxConnection, null));
    }

    /** 新增，并返回自增主键 */
    public <T> Future<Long> insert(Future<Transaction> transactionFuture, SQL sql) {
        String sqlString = sql.getSql();
        Map<String, Object> params = sql.getParams();
        log.debug(sqlString);
        log.debug(params.toString());
        return transactionFuture.compose(transaction -> SqlTemplate.forUpdate(transaction.getVertxConnection(), sqlString).execute(params).map(rs -> rs.property(MySQLClient.LAST_INSERTED_ID)));
    }

    /** 单条DML语句：增、删、改。返回受影响行数 */
    public <T> Future<Integer> update(Future<Transaction> transactionFuture, SQL sql) {
        String sqlString = sql.getSql();
        Map<String, Object> params = sql.getParams();
        log.debug(sqlString);
        log.debug(params.toString());
        return transactionFuture.compose(transaction -> SqlTemplate.forUpdate(transaction.getVertxConnection(), sqlString).execute(params).map(rs -> rs.rowCount()));
    }

    /** 查询List */
    public <T> Future<List<T>> selectList(Future<Transaction> transactionFuture, SQL sql, Class<T> resultClass) {
        String sqlString = sql.getSql();
        Map<String, Object> params = sql.getParams();
        log.debug(sqlString);
        log.debug(params.toString());
        return transactionFuture.compose(transaction -> SqlTemplate.forQuery(transaction.getVertxConnection(), sqlString).mapTo(resultClass).execute(params).map(rs -> {
            List<T> resultList = new ArrayList<>(rs.rowCount());
            for (T item : rs) {
                resultList.add(item);
            }
            return resultList;
        }));
    }

    /** 查询Item */
    public <T> Future<T> selectOne(Future<Transaction> transactionFuture, SQL sql, Class<T> resultClass) {
        String sqlString = sql.getSql();
        Map<String, Object> params = sql.getParams();
        log.debug(sqlString);
        log.debug(params.toString());
        return transactionFuture.compose(transaction -> SqlTemplate.forQuery(transaction.getVertxConnection(), sqlString).mapTo(resultClass).execute(params).map(rs -> {
            if (rs.size() <= 0) {
                return null;
            } else if (rs.size() > 1) {
                throw new RuntimeException("find " + rs.rowCount() + " rows, is than one: " + sql + " " + params);
            }
            return rs.iterator().next();
        }));
    }

    /** 单查询一个Count */
    public <T> Future<T> selectOneColumn(Future<Transaction> transactionFuture, SQL sql, Class<T> resultClass) {
        String sqlString = sql.getSql();
        Map<String, Object> params = sql.getParams();
        log.debug(sqlString);
        log.debug(params.toString());
        return transactionFuture.compose(transaction -> SqlTemplate.forQuery(transaction.getVertxConnection(), sqlString).execute(params).map(rs -> {
            if (rs.rowCount() < 0) {
                return null;
            } else if (rs.rowCount() > 1) {
                throw new RuntimeException("find " + rs.rowCount() + " rows, is than one: " + sql + " " + params);
            }
            return rs.iterator().next().get(resultClass, 0);
        }));
    }

    /** 提交一个事务 */
    public Future<Transaction> commit(Future<Transaction> transactionFuture) {
        return transactionFuture.onComplete(transaction -> {
            if (transaction.result().getVertxTransaction() == null) {
                transaction.result().getVertxConnection().close();
            } else {
                transaction.result().getVertxTransaction().commit().onComplete(v -> transaction.result().getVertxConnection().close());
            }
        });
    }

    /** 回滚一个事务 */
    public Future<Transaction> rollback(Future<Transaction> transactionFuture) {
        return transactionFuture.onComplete(transaction -> {
            if (transaction.result().getVertxTransaction() == null) {
                transaction.result().getVertxConnection().close();
            } else {
                transaction.result().getVertxTransaction().rollback().onComplete(v -> transaction.result().getVertxConnection().close());
            }
        });
    }
}
